#include "StdAfx.h"
#include "SkypeAPICtrl.h"
#include "Utility.h"
#include "ClientsManager.h"
SkypeAPICtrl::SkypeAPICtrl(ClientsManager* clientMan) :
			m_clientMan(clientMan)
			//,hGlobal_SkypeAPIWindowHandle(NULL)
			, m_threadRunning(true), m_hInitMainWindowHandle(NULL),
			m_threadHandle(NULL), m_isReady(false) {
	InitializeCriticalSection(&m_sendMessageCriticalSection);
	m_uiMsgIDSkypeControlAPIAttach = RegisterWindowMessage(
			L"SkypeControlAPIAttach");
	m_uiMsgIDSkypeControlAPIDiscover = RegisterWindowMessage(
			L"SkypeControlAPIDiscover");
	if (m_uiMsgIDSkypeControlAPIAttach != 0 && m_uiMsgIDSkypeControlAPIDiscover
			!= 0) {
		if (Initialize_CreateWindowClass()) {
			if (Initialize_CreateMainWindow()) {
				m_isReady = true;
				//StartMessageLoop();
			}
		}
	} else {
		m_isReady = false;
		throw("SkypeAPICtrl init failed");
	}
}

SkypeAPICtrl::~SkypeAPICtrl(void) {
	if (m_threadHandle) {
		TerminateThread(m_threadHandle, 0);
		m_threadHandle = 0;
	}
	DeInitialize_DestroyMainWindow();
	DeInitialize_DestroyWindowClass();
	DeleteCriticalSection(&m_sendMessageCriticalSection);
}

map<void*, SkypeAPICtrl*> SkypeAPICtrl::m_winInstMap;

// tab size: 2
// following special commands are recognized and handled by this client
//   #quit
//   #exit
//     terminate client
//   #dbgon
//     turn on debug printing of windows messages
//   #dbgoff
//     turn off debug printing of windows messages
//   #connect
//     discover skype api
//   #disconnect
//     terminate connection to skype api
// all other commands are sent "as is" directly to Skype
// (no UTF-8 translation is done at present)

#include <conio.h>
#include <stdio.h>
#include <string.h>
#include <process.h>
#include <iostream>
#include <string>
#include <sstream>
#include <iomanip>
#include <map>
using namespace std;

#if defined(_DEBUG)
static bool volatile fGlobal_DumpWindowsMessages=true;
#else
static bool volatile fGlobal_DumpWindowsMessages = false;
#endif
static DWORD ulGlobal_PromptConsoleMode = 0;
static HANDLE volatile hGlobal_PromptConsoleHandle = NULL;

static bool Global_Console_ReadRow(char *pacPromptBuffer,
		unsigned int uiMaxLength) {
	HANDLE hConsoleHandle, hDuplicatedConsoleHandle;
	DWORD ulCharactersRead, ulConsoleMode;
	unsigned int uiNewLength;
	BOOL fReadConsoleResult;
	bool fReturnStatus;
	char cCharacter;

	fReturnStatus = false;
	while ((hConsoleHandle = GetStdHandle(STD_INPUT_HANDLE))
			!=INVALID_HANDLE_VALUE) {
		if (DuplicateHandle(GetCurrentProcess(), hConsoleHandle,
				GetCurrentProcess(), &hDuplicatedConsoleHandle, 0, FALSE,
				DUPLICATE_SAME_ACCESS) == FALSE)
			break;
		GetConsoleMode(hDuplicatedConsoleHandle, &ulConsoleMode);
		SetConsoleMode(hDuplicatedConsoleHandle, ENABLE_LINE_INPUT
				| ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT);
		hGlobal_PromptConsoleHandle = hDuplicatedConsoleHandle;
		ulGlobal_PromptConsoleMode = ulConsoleMode;
		fReadConsoleResult = ReadConsole(hGlobal_PromptConsoleHandle,
				(LPVOID) pacPromptBuffer, uiMaxLength, &ulCharactersRead, NULL);
		if (hGlobal_PromptConsoleHandle == (HANDLE) 0)
			break;
		hGlobal_PromptConsoleHandle = (HANDLE) 0;
		SetConsoleMode(hDuplicatedConsoleHandle, ulConsoleMode);
		CloseHandle(hDuplicatedConsoleHandle);
		if (fReadConsoleResult == FALSE || ulCharactersRead > uiMaxLength)
			break;
		pacPromptBuffer[ulCharactersRead] = 0;
		uiNewLength = ulCharactersRead;
		while (uiNewLength != 0) {
			cCharacter = pacPromptBuffer[uiNewLength - 1];
			if (cCharacter != '\r' && cCharacter != '\n')
				break;
			uiNewLength--;
		}
		pacPromptBuffer[uiNewLength] = 0;
		fReturnStatus = true;
		break;
	}
	if (fReturnStatus == false)
		pacPromptBuffer[0] = 0;
	return (fReturnStatus);
}

static void Global_Console_CancelReadRow(void) {
	if (hGlobal_PromptConsoleHandle != (HANDLE) 0) {
		SetConsoleMode(hGlobal_PromptConsoleHandle, ulGlobal_PromptConsoleMode);
		CloseHandle(hGlobal_PromptConsoleHandle);
		hGlobal_PromptConsoleHandle = (HANDLE) 0;
	}
}

static LRESULT APIENTRY SkypeAPITest_Windows_WindowProc(HWND hWindow,
		UINT uiMessage, WPARAM uiParam, LPARAM ulParam) {
	SkypeAPICtrl* ctrl = SkypeAPICtrl::instanceOfWin((void*) hWindow);
	if (ctrl) {
		return ctrl->SkypeAPIWindowProc(hWindow, uiMessage, uiParam, ulParam);
	} else {
		DefWindowProc(hWindow, uiMessage, uiParam, ulParam);
		return true;
	}
}

long SkypeAPICtrl::SkypeAPIWindowProc(void * hWindow, unsigned int uiMessage,
		unsigned int uiParam, unsigned int ulParam) {
	LRESULT lReturnCode;
	bool fIssueDefProc;

	lReturnCode = 0;
	fIssueDefProc = false;
	switch (uiMessage) {
	case WM_DESTROY:
		m_hInitMainWindowHandle = NULL;
		PostQuitMessage(0);
		break;
	case WM_COPYDATA:
		//if( hGlobal_SkypeAPIWindowHandle==(HWND)uiParam )
	{
		PCOPYDATASTRUCT poCopyData = (PCOPYDATASTRUCT) ulParam;
		printf("Message from Skype(%u): %.*s\n", poCopyData->dwData,
				poCopyData->cbData, poCopyData->lpData);
		if (m_clientMan) {
			m_clientMan->attachApiMessage(this, (HWND) uiParam, poCopyData);
		}
		lReturnCode = 1;
	}
		break;
	default:
		if (uiMessage == m_uiMsgIDSkypeControlAPIAttach) {
			switch (ulParam) {
			case SKYPECONTROLAPI_ATTACH_SUCCESS: {
				printf(
						"!!! Connected HWND(0x%x); to terminate issue #disconnect\n",
						uiParam);
				//hGlobal_SkypeAPIWindowHandle=(HWND)uiParam;
				if (m_clientMan) {
					m_clientMan->attachApiSuccess(this, (HWND) uiParam);
				}
			}
				break;
			case SKYPECONTROLAPI_ATTACH_PENDING_AUTHORIZATION:
				printf("!!! Pending authorization\n");
				if (m_clientMan) {
					m_clientMan->attachApiAuthPending(this);
				}
				break;
			case SKYPECONTROLAPI_ATTACH_REFUSED:
				printf("!!! Connection refused\n");
				if (m_clientMan) {
					m_clientMan->attachApiRefused(this);
				}
				break;
			case SKYPECONTROLAPI_ATTACH_NOT_AVAILABLE:
				printf("!!! Skype API not available\n");
				if (m_clientMan) {
					m_clientMan->attachApiNotAvailable(this);
				}
				break;
			case SKYPECONTROLAPI_ATTACH_API_AVAILABLE:
				printf("!!! Try connect now (API available); issue #connect\n");
				if (m_clientMan) {
					m_clientMan->attachApiAvailable(this);
				}
				break;
			}
			lReturnCode = 1;
			break;
		}
		fIssueDefProc = true;
		break;
	}
	if (fIssueDefProc)
		lReturnCode
				= DefWindowProc((HWND) hWindow, uiMessage, uiParam, ulParam);
	if (fGlobal_DumpWindowsMessages) {
		printf(
				"WindowProc: hWindow=0x%08X, MainWindow=0x%08X, Message=%5u, WParam=0x%08X, LParam=0x%08X; Return=%ld%s\n",
				hWindow, m_hInitMainWindowHandle, uiMessage, uiParam, ulParam,
				lReturnCode, fIssueDefProc ? " (default)" : "");
	}
	return (lReturnCode);
}

bool SkypeAPICtrl::Initialize_CreateWindowClass(void) {
	bool fReturnStatus;

	fReturnStatus = false;
	m_hInitProcessHandle = OpenProcess(PROCESS_DUP_HANDLE, FALSE,
			GetCurrentProcessId());
	if (m_hInitProcessHandle != NULL) {
		WNDCLASS oWindowClass;

		wcscpy(m_initWindowClassName, L"Skype-AutoCall");

		oWindowClass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
		oWindowClass.lpfnWndProc = (WNDPROC) &SkypeAPITest_Windows_WindowProc;
		oWindowClass.cbClsExtra = 0;
		oWindowClass.cbWndExtra = 0;
		oWindowClass.hInstance = (HINSTANCE) m_hInitProcessHandle;
		oWindowClass.hIcon = NULL;
		oWindowClass.hCursor = NULL;
		oWindowClass.hbrBackground = NULL;
		oWindowClass.lpszMenuName = NULL;
		oWindowClass.lpszClassName = m_initWindowClassName;

		if (RegisterClass(&oWindowClass) != 0)
			fReturnStatus = true;

	}
	if (fReturnStatus == false)
		CloseHandle((HANDLE) m_hInitProcessHandle), m_hInitProcessHandle = NULL;
	return (fReturnStatus);
}

void SkypeAPICtrl::DeInitialize_DestroyWindowClass(void) {
	UnregisterClass(m_initWindowClassName, (HINSTANCE) m_hInitProcessHandle);
	CloseHandle((HANDLE) m_hInitProcessHandle), m_hInitProcessHandle = NULL;
}

bool SkypeAPICtrl::Initialize_CreateMainWindow(void) {
	m_hInitMainWindowHandle = CreateWindowEx(
			WS_EX_APPWINDOW | WS_EX_WINDOWEDGE, m_initWindowClassName, L"",
			WS_BORDER | WS_SYSMENU | WS_MINIMIZEBOX, CW_USEDEFAULT,
			CW_USEDEFAULT, 128, 128, NULL, 0, (HINSTANCE) m_hInitProcessHandle,
			0);
	m_winInstMap[(void*) m_hInitMainWindowHandle] = this;
	return (m_hInitMainWindowHandle != NULL ? true : false);
}

void SkypeAPICtrl::DeInitialize_DestroyMainWindow(void) {
	if (m_hInitMainWindowHandle != NULL) {
		DestroyWindow((HWND) m_hInitMainWindowHandle);
		m_winInstMap.erase(m_hInitMainWindowHandle);
		m_hInitMainWindowHandle = NULL;
	}
}

DWORD WINAPI MessageLoopThreadProc(LPVOID lpParam) {
	MSG oMessage;

	while (GetMessage(&oMessage, 0, 0, 0) != FALSE) {
		TranslateMessage(&oMessage);
		DispatchMessage(&oMessage);
	}
	return 0;
}
void SkypeAPICtrl::StartMessageLoop(void) {
	m_threadHandle
			= CreateThread(NULL, 0, MessageLoopThreadProc, NULL, 0, NULL);
}
/*
 void __cdecl SkypeAPICtrl::Global_InputProcessingThread(void *)
 {
 static char acInputRow[1024];
 bool fProcessed;

 if( SendMessage( HWND_BROADCAST, m_uiMsgIDSkypeControlAPIDiscover, (WPARAM)m_hInitMainWindowHandle, 0)!=0 )
 {
 while(Global_Console_ReadRow( acInputRow, sizeof(acInputRow)-1))
 {
 if( stricmp( acInputRow, "#quit")==0 ||
 stricmp( acInputRow, "#exit")==0 )
 break;
 fProcessed=false;
 if( stricmp( acInputRow, "#dbgon")==0 )
 {
 printf( "SkypeControlAPIAttach=%u, SkypeControlAPIDiscover=%u, m_hInitMainWindowHandle=0x%08lX\n",
 m_uiMsgIDSkypeControlAPIAttach, m_uiMsgIDSkypeControlAPIDiscover, m_hInitMainWindowHandle);
 fGlobal_DumpWindowsMessages=true,fProcessed=true;
 }
 if( stricmp( acInputRow, "#dbgoff")==0 )
 fGlobal_DumpWindowsMessages=false,fProcessed=true;
 if( stricmp( acInputRow, "#connect")==0 )
 {
 SendMessage( HWND_BROADCAST, m_uiMsgIDSkypeControlAPIDiscover, (WPARAM)m_hInitMainWindowHandle, 0);
 fProcessed=true;
 }
 if( stricmp( acInputRow, "#disconnect")==0 )
 {
 hGlobal_SkypeAPIWindowHandle=NULL;
 printf("!!! Disconnected\n");
 fProcessed=true;
 }
 if( stricmp( acInputRow, "#conn_win")==0 )
 {
 if(Global_Console_ReadRow( acInputRow, sizeof(acInputRow)-1))
 {
 string s(acInputRow);
 unsigned int hwnd;
 istringstream iss(s);
 iss >>setbase(ios_base::hex)>> hwnd;
 cout<<(void*)hwnd<<endl;
 SendMessage( (HWND)hwnd, m_uiMsgIDSkypeControlAPIDiscover, (WPARAM)m_hInitMainWindowHandle, 0);
 fProcessed=true;
 }
 else
 {
 SendMessage( HWND_BROADCAST, m_uiMsgIDSkypeControlAPIDiscover, (WPARAM)m_hInitMainWindowHandle, 0);
 fProcessed=true;
 }
 }
 if( acInputRow[0] == '$' )
 {
 COPYDATASTRUCT oCopyData;

 // send command to skype
 oCopyData.dwData=0;
 oCopyData.lpData=acInputRow+1;
 oCopyData.cbData=strlen(acInputRow)+1;
 if( SendMessage( hGlobal_SkypeAPIWindowHandle, WM_COPYDATA, (WPARAM)m_hInitMainWindowHandle, (LPARAM)&oCopyData)==FALSE )
 {
 cout<<acInputRow<<endl;
 }
 }
 if( stricmp( acInputRow, "#test")==0 )
 {
 COPYDATASTRUCT oCopyData;

 // send command to skype
 strcpy(acInputRow, "OPEN DIALPAD");
 oCopyData.dwData=0;
 oCopyData.lpData=acInputRow;
 oCopyData.cbData=strlen(acInputRow)+1;
 if( SendMessage( hGlobal_SkypeAPIWindowHandle, WM_COPYDATA, (WPARAM)m_hInitMainWindowHandle, (LPARAM)&oCopyData)==FALSE )
 {
 cout<<acInputRow<<endl;
 }
 }
 if( fProcessed==false && hGlobal_SkypeAPIWindowHandle!=NULL )
 {
 COPYDATASTRUCT oCopyData;

 // send command to skype
 oCopyData.dwData=0;
 oCopyData.lpData=acInputRow;
 oCopyData.cbData=strlen(acInputRow)+1;
 if( oCopyData.cbData!=1 )
 {
 if( SendMessage( hGlobal_SkypeAPIWindowHandle, WM_COPYDATA, (WPARAM)m_hInitMainWindowHandle, (LPARAM)&oCopyData)==FALSE )
 {
 hGlobal_SkypeAPIWindowHandle=NULL;
 printf("!!! Disconnected\n");
 }
 }
 }
 }
 }
 PostMessage( m_hInitMainWindowHandle, WM_CLOSE, 0, 0);
 SetEvent(m_threadShutdownEvent);
 m_threadRunning=false;
 }
 */
void SkypeAPICtrl::test(void) {
	// create window class
	//   create dummy/hidden window for processing messages
	//     run message loop thread
	//       do application control until exit
	//       exit: send QUIT message to our own window
	//             wait until thred terminates
	//   destroy main window
	// destroy window class
	m_uiMsgIDSkypeControlAPIAttach = RegisterWindowMessage(
			L"SkypeControlAPIAttach");
	m_uiMsgIDSkypeControlAPIDiscover = RegisterWindowMessage(
			L"SkypeControlAPIDiscover");
	if (m_uiMsgIDSkypeControlAPIAttach != 0 && m_uiMsgIDSkypeControlAPIDiscover
			!= 0) {
		if (Initialize_CreateWindowClass()) {
			if (Initialize_CreateMainWindow()) {
				m_threadShutdownEvent = (unsigned long) CreateEvent(NULL, TRUE,
						FALSE, NULL);
				if (m_threadShutdownEvent != NULL) {
					// 					if( _beginthread( &Global_InputProcessingThread, 64*1024, NULL)!=(unsigned long)-1 )
					// 					{
					// 						Global_MessageLoop();
					// 						Global_Console_CancelReadRow();
					// 						WaitForSingleObject( m_threadShutdownEvent, INFINITE);
					// 					}
					// 					CloseHandle(m_threadShutdownEvent);
				}
				DeInitialize_DestroyMainWindow();
			}
			DeInitialize_DestroyWindowClass();
		}
	}
}

void SkypeAPICtrl::scanCtrlWin() {
	EnterCriticalSection(&m_sendMessageCriticalSection);
	DWORD result;
	SendMessageTimeout(HWND_BROADCAST, m_uiMsgIDSkypeControlAPIDiscover,
			(WPARAM) m_hInitMainWindowHandle, 0, SMTO_ABORTIFHUNG, 1000,
			&result);
	LeaveCriticalSection(&m_sendMessageCriticalSection);
}

LRESULT SkypeAPICtrl::sendMessage(HWND targetWin, const string& msg) {
	COPYDATASTRUCT oCopyData;

	// send command to skype
	oCopyData.dwData = 0;
	oCopyData.lpData = (PVOID) msg.c_str();
	oCopyData.cbData = msg.length() + 1;
	DWORD result;
	LRESULT ret = SendMessageTimeout(targetWin, WM_COPYDATA,
			(WPARAM) m_hInitMainWindowHandle, (LPARAM) &oCopyData,
			SMTO_ABORTIFHUNG, 1000, &result);
	return ret;

}
